package com.kingsoft.dc.khaos.extender.meta.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kingsoft.dc.khaos.extender.exception.MetaException;
import com.kingsoft.dc.khaos.extender.meta.model.MetaParams;
import com.kingsoft.dc.khaos.extender.meta.model.TenantInfo;
import com.kingsoft.dc.khaos.extender.meta.model.auth.DmAuth;
import com.kingsoft.dc.khaos.extender.meta.model.check.DmCheck;
import com.kingsoft.dc.khaos.extender.meta.model.check.ESCheck;
import com.kingsoft.dc.khaos.extender.meta.model.check.MQCheck;
import com.kingsoft.dc.khaos.extender.meta.model.col.DmTableColumn;
import com.kingsoft.dc.khaos.extender.meta.model.db.DmDatabase;
import com.kingsoft.dc.khaos.extender.meta.model.table.DmTable;
import com.kingsoft.dc.khaos.extender.meta.utils.HttpUtils;
import com.ksyun.auth.client.AuthenticationHelper;
import com.ksyun.auth.client.Authorize;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.netty.handler.codec.http.HttpHeaders.Values.APPLICATION_JSON;

/**
 * Created by jing on 19/6/27.
 */
public class DmApiUtils {
    //21202F2938212B3E22272626252E434D
    private static Logger logger = LoggerFactory.getLogger(DmApiUtils.class);
    //元数据查询接口-查询数据源列表
    final static String QUERY_DATASOURCE_LIST = "ds/sourceList/simply";

    //元数据查询接口-数据源物理地址获取
    final static String QUERY_PHYSICALINFO_PATH = "ds/source/physicalInfo";

    //元数据查询接口-库表查询-查询表信息列表-详情
    final static String QUERY_TABLE_LIST_FULL_PATH = "query/table/list/full";

    //元数据查询接口-库表查询-查询表字段信息列表-详情
    final static String QUERY_TABLE_COLUMN_LIST_FULL_PATH = "query/tableColumn/list/full";

    //元数据查询接口-库表查询-查询库信息列表-详情
    final static String QUERY_DB_LIST_FULL_PATH = "query/db/list/full";

    //权限查询接口-数据权限鉴权-项目鉴权（快速响应）
    final static String DMGRGQUERY_DATAMANAGER_RIGT = "datamanager/right/authority/project/quick";

    //数据质量规则查询接口-根据规则id查询规则配置
    final static String QUERY_DATAQUALITY_CHECK_RULE = "data/quality/rule/getByIds";

    //数据质量规则查询接口-根据规则id查询设计态表规则
    final static String QUERY_DESIGN_DATAQUALITY_CHECK_RULE = "data/quality/rule/getDesignTableByIds";

    //数据质量配置查询接口-数据源配置信息查询接口(ES)
    final static String QUERY_DATAQUALITY_ES_INFO = "data/quality/analysis/dataSourceInfo";

    //数据质量配置查询接口-MQ配置信息查询接口
    final static String QUERY_DATAQUALITY_MQ_INFO = "data/quality/analysis/mqConfig";
    final static String AKSK_PATH = "api/aksk";
    final static String PROTOCOL = "http";

    //元数据查询接口-数据源配置获取
    final static String QUERY_DATASOURCE_INFO_PATH = "ds/source/dsInfo";

    //元数据查询接口-库表查询-查询表分表信息列表-详情
    final static String QUERY_TABLESPLIT_LIST_FULL_PATH = "query/tableSplit/list/full";

    //元数据查询接口-库表查询-查询物理分表
    final static String QUERY_TABLE_LIST_SPLIT_BRIEF_PATH = "query/table/list/split/brief";

    //元数据管理接口-库表管理-创建物理分表
    final static String QUERY_TABLE_LIST_SPLIT_CREATE_PATH = "table/list/split/create";
    final static String QUERY_DSINFO_PATH = "ds/source/dsInfo";

    //元数据管理接口-数据热度统计上报-数据状态批量登记
    final static String REPORT_DATA_STATUS_PATH = "report/data/status/batch";

    //元数据查询接口-获取表分区
    final static String QUERY_TABLE_PARTITION_LIST = "query/tablePartition/list/brief";

    /**
     * @param request
     * @return
     */
    public static TenantInfo getTenantAkSk(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl() + "/" + AKSK_PATH + "/" + request.getTenantId()));
        String content = getData(request);
        String result = extractResult(content);
        TenantInfo tenantInfo = JSON.parseObject(result, TenantInfo.class);
        return tenantInfo;
    }

    /**
     * 获取用户鉴权信息
     *
     * @param request
     * @return
     */
    public static DmAuth getProjectAuth(DmRequest request) {
        logger.info("开始校验权限");
        request.setRealUrl(addProtocol(request.getUrl().replace("**", DMGRGQUERY_DATAMANAGER_RIGT)));
        MetaParams metaParams = request.getMetaParams();
        String params = metaParams.getAuthParams();
        request.setParams(metaParams.getAuthParams());
        String content = postData(request);
        boolean success = checkResponseIfSuccess(content);
        if (success) {
            DmAuth da = JSON.parseObject(content, DmAuth.class);
            boolean hasPriv = da.getTotalPrivs();
            if (hasPriv) {
                logger.info("权限校验结果为通过");
            } else {
                logger.info("此项目没有足够的权限访问如下库表。数据表[{}.{}.{}],被拒绝的权限:{}", metaParams.getDsName(), metaParams.getDbName(), metaParams.getTblName(), metaParams.getPrivSet());
            }
            return da;
        } else {
            Map<String, Object> resultMap = JSON.parseObject(content, Map.class);
            String message = resultMap.containsKey("message") ? resultMap.get("message").toString() : "";
            logger.error("请求权限认证接口返回数据异常:{}" + message);
            throw new MetaException("请求权限认证接口返回数据异常");
        }
    }

    public static DmDatasourceResult getDatasource(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_PHYSICALINFO_PATH)));
        request.setParams(metaParams.getDatasourceParams());
        String content = postData(request);
        String result = extractResult(content);
        logger.info("getDatasource result:{}",result);
        List<DmDatasourceResult> dsResult = JSONObject.parseArray(result, DmDatasourceResult.class);
        if (dsResult.size() > 0) {
            return dsResult.get(0);
        } else {
            return null;
        }
    }

    /**
     * 根据数据源ID获取数据源地址
     * request参数构建
     * 方法1:request.setParams("{\"env\":\"TEST\":\"dsIds\":[1],\"privilege\":\"read\"}
     * 方法2:
     * MetaParams metaParams = new MetaParams();
     * metaParams.buildReadDatasourceParams(run_env, dsId);
     * request.setMetaParams(metaParams);
     *
     * @param request
     * @return
     */
    public static DmDatasourceResult getDatasourceById(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DATASOURCE_INFO_PATH)));
        if (request.getParams() == null) {
            request.setParams(metaParams.getDsIdParams());
        }
        String content = postData(request);
        String result = extractResult(content);
        List<DmDatasourceResult> dsResult = JSONObject.parseArray(result, DmDatasourceResult.class);
        if (dsResult.size() > 0) {
            return dsResult.get(0);
        } else {
            return null;
        }
    }


    /**
     * 根据数据源name获取数据源地址
     * request参数构建
     * 方法1:request.setParams("{\"env\":\"TEST\":\"dsNames\":[1],\"privilege\":\"read\"}
     * 方法2:
     * MetaParams metaParams = new MetaParams();
     * metaParams.buildReadDatasourceParams(run_env, dsName);
     * request.setMetaParams(metaParams);
     *
     * @param request
     * @return
     */
    public static DmDatasourceResult getDatasourceByName(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DATASOURCE_INFO_PATH)));
        if (request.getParams() == null) {
            request.setParams(metaParams.getDsNameParams());
        }
        String content = postData(request);
        String result = extractResult(content);
        List<DmDatasourceResult> dsResult = JSONObject.parseArray(result, DmDatasourceResult.class);
        if (dsResult.size() > 0) {
            return dsResult.get(0);
        } else {
            return null;
        }
    }

    /**
     * 获取数据源列表
     *
     * @param request
     * @return
     */
    public static DatasourceListResult getDatasourceList(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DATASOURCE_LIST)));
        request.setParams(metaParams.getDatasourceListParams());
        String content = postData(request);
        String result = extractResult(content);
        List<DatasourceListResult> dsResult = JSONObject.parseArray(result, DatasourceListResult.class);
        if (dsResult != null && dsResult.size() > 0) {
            return dsResult.get(0);
        } else {
            return null;
        }
    }

    public static DmDatabase getDatabase(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DB_LIST_FULL_PATH)));
        request.setParams(metaParams.getDatabaseParams());
        String content = postData(request);
        String result = extractResult(content);
        List<DmDatabase> dsResult = JSONObject.parseArray(result, DmDatabase.class);
        if (dsResult.size() > 0) {
            return dsResult.get(0);
        } else {
            return null;
        }
    }

    public static DmTable getTable(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_TABLE_LIST_FULL_PATH)));
        request.setParams(metaParams.getTableParams());
        String content = postData(request);
        String result = extractResult(content);
        List<DmTable> dsResult = JSONObject.parseArray(result, DmTable.class);
        if (dsResult.size() > 0) {
            return dsResult.get(0);
        } else {
            return null;
        }
    }

    public static List<DmTableColumn> getColumns(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_TABLE_COLUMN_LIST_FULL_PATH)));
        MetaParams metaParams = request.getMetaParams();
        request.setParams(metaParams.getTableColumnParams());
        String content = postData(request);
        String result = extractResult(content);
        List<DmTableColumn> dsResult = JSONObject.parseArray(result, DmTableColumn.class);
        return dsResult;
    }

    public static String extractResult(String content) {
        String result = "";
        Map contentMap = JSON.parseObject(content, Map.class);
        if (contentMap.containsKey("status")) {
            int status = Integer.parseInt(contentMap.get("status").toString());
            if (status == 200) {
                result = contentMap.get("result").toString();
            } else {
                logger.warn("the response status not 200,status={}, message={}", contentMap.get("status"), contentMap.get("message"));
            }
        }
        return result;
    }

    public static boolean checkResponseIfSuccess(String content) {
        boolean success = false;
        Map contentMap = JSON.parseObject(content, Map.class);
        if (contentMap.containsKey("status")) {
            int status = Integer.parseInt(contentMap.get("status").toString());
            if (status == 200) {
                success = true;
            } else {
                logger.error("message={}", contentMap.get("message"));
            }
        }
        return success;
    }

    /**
     * 批量校验权限
     *
     * @param request
     * @return
     */
    public static DmAuthResult getProjectAuthDetail(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", DMGRGQUERY_DATAMANAGER_RIGT)));
        String params = request.getTableAuthParams().getAuthParams();
        request.setParams(params);
        String content = postData(request);
        if (content == null || content.isEmpty()) {
            throw new MetaException("请求权限认证接口异常，接口未返回任何数据");
        }
        DmAuthResult result = JSON.parseObject(content, DmAuthResult.class);
        return result;
    }

    public static String postData(DmRequest request) {
        String content = "";
        if (request.isNeedAuth()) {
            logger.info("请求RealUrl：{}",request.getRealUrl());
            logger.info("请求headers：{}",getAuthHeader(request.getAuthRequest()));
            logger.info("请求params：{}",request.getParams());
            content = HttpUtils.doPost(request.getRealUrl(), getAuthHeader(request.getAuthRequest()), request.getParams());
        } else {
            content = HttpUtils.doPost(request.getRealUrl(), null, request.getParams());
        }
        logger.info("post response content:==>{}" , content);
        return content;

    }


    public static String getData(DmRequest request) {
        String content = "";
        if (request.isNeedAuth()) {
            content = HttpUtils.doGet(request.getRealUrl(), getAuthHeader(request.getAuthRequest()));
        } else {
            content = HttpUtils.doGet(request.getRealUrl(), null);
        }
//        logger.info("get response content:==>{}" , content);
        return content;

    }

    public static String addTenantId(String url) {
        if (!url.startsWith(PROTOCOL)) {
            url = PROTOCOL + "://" + url;
        }
        return url;
    }

    public static String addProtocol(String url) {
        if (!url.startsWith(PROTOCOL)) {
            url = PROTOCOL + "://" + url;
        }
        return url;
    }

    @Authorize(Authorize.Type.PROJECT)
    public static Map getAuthHeader(AuthRequest authRequest) {
        String ak = authRequest.getAk();
        String sk = authRequest.getSk();
        String authServerUrl = authRequest.getAuthUrl();
//        AuthenticationHelper.setDefaultAuthorizeMethod(Authorize.Type.PROJECT);
        AuthenticationHelper.getHeaderInterceptor(ak, sk, authServerUrl);
        String token = AuthenticationHelper.getToken();
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("Content-Type", APPLICATION_JSON);
        headerMap.put("X-AUTH-APP", ak);
        headerMap.put("X-AUTH-TOKEN", token);
        headerMap.put("SOURCE-AK", ak);
        headerMap.put("AUTH-PROJECT-ID", String.valueOf(authRequest.getAuthProjectId()));
        return headerMap;
    }

    /**
     * 获取数据质量检核规则
     *
     * @param request
     * @return
     */
    public static List<DmCheck> getCheckRule(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DATAQUALITY_CHECK_RULE)));
        String params = request.getParams();
        String content = postData(request);
//        logger.info("check rule request params:==>{}", params);
//        logger.info("check rule response content:==>{}", content);
        String result = extractResult(content);
        List<DmCheck> checkRuleResult = JSONObject.parseArray(result, DmCheck.class);
        return checkRuleResult;
    }

    //create by liuyg   获取设计态表规则，试运行时使用该方法调用数据管理接口
    public static List<DmCheck> getDesignCheckRule(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DESIGN_DATAQUALITY_CHECK_RULE)));
        String content = postData(request);
        String result = extractResult(content);
        List<DmCheck> checkRuleResult = JSONObject.parseArray(result, DmCheck.class);
        return checkRuleResult;
    }

    /**
     * 获取数据质量检核上报ES配置信息
     *
     * @param request
     * @return
     */
    public static ESCheck getESCheckInfo(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DATAQUALITY_ES_INFO)));
        String params = request.getParams();
        String content = postData(request);
//        logger.info("ES check info request params:==>{}", params);
//        logger.info("ES check info response content:==>{}", content);
        String result = extractResult(content);
        ESCheck esCheckInfo = JSONObject.parseObject(result, ESCheck.class);
        return esCheckInfo;
    }

    /**
     * 获取数据质量检核上报MQ配置信息
     *
     * @param request
     * @return
     */
    public static MQCheck getMQCheckInfo(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DATAQUALITY_MQ_INFO)));
        String params = request.getParams();
        String content = postData(request);
//        logger.info("MQ check info request params:==>{}", params);
//        logger.info("MQ check info response content:==>{}", content);
        String result = extractResult(content);
        MQCheck mqCheckInfo = JSONObject.parseObject(result, MQCheck.class);
        return mqCheckInfo;
    }

    /**
     * 获取分表信息
     *
     * @param request
     * @return
     */
    public static DmTableSplit getTableSplit(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_TABLESPLIT_LIST_FULL_PATH)));
        request.setParams(metaParams.getTableSplitParams());
        String content = postData(request);
        String result = extractResult(content);
        List<DmTableSplit> dmTableSplit = JSONObject.parseArray(result, DmTableSplit.class);
        if (dmTableSplit != null && dmTableSplit.size() > 0) {
            return dmTableSplit.get(0);
        } else {
            return null;
        }
    }

    /**
     * 获取分表的真实表名信息
     *
     * @param request
     * @return
     */
    public static DmTableSplitResult getRealTable(DmRequest request) {

        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_TABLE_LIST_SPLIT_BRIEF_PATH)));

        String content = postData(request);
        String result = extractResult(content);
        List<DmTableSplitResult> dmTableSplitResult = JSONObject.parseArray(result, DmTableSplitResult.class);
        if (dmTableSplitResult != null && dmTableSplitResult.size() > 0) {
            return dmTableSplitResult.get(0);
        } else {
            return null;
        }
    }

    /**
     * 获取分表的真实表名信息 (用于日期分表下的sink 调用的是create 建表接口)
     *
     * @param request
     * @return
     */
    public static DmTableSplitResult getRealTableWithDateTimeAndWrite(DmRequest request) {
        // 写日期分表
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_TABLE_LIST_SPLIT_CREATE_PATH)));

        String content = postData(request);
        String result = extractResult(content);
        List<DmTableSplitResult> dmTableSplitResult = JSONObject.parseArray(result, DmTableSplitResult.class);
        if (dmTableSplitResult !=null && dmTableSplitResult.size() > 0) {
            return dmTableSplitResult.get(0);
        } else {
            return null;
        }
    }

    /**
     * 获取非托管数据源信息 (redis用)
     *
     * @param request
     * @return
     */
    public static DmDatasourceResult getUnmanagedDatasource(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_DSINFO_PATH)));
        String content = postData(request);
        String result = extractResult(content);
        List<DmDatasourceResult> dmTableSplitResult = JSONObject.parseArray(result, DmDatasourceResult.class);
        if (dmTableSplitResult.size() > 0) {
            return dmTableSplitResult.get(0);
        } else {
            return null;
        }
    }

    /**
     * 上报数据状态
     *
     * @param request
     * @return
     */
    public static ReportDataStatusResult reportDataStatus(DmRequest request) {
        request.setRealUrl(addProtocol(request.getUrl().replace("**", REPORT_DATA_STATUS_PATH)));
        String content = postData(request);
        //String result = extractResult(content);
        Map contentMap = JSON.parseObject(content, Map.class);
        if (contentMap.containsKey("status")) {
            int status = Integer.parseInt(contentMap.get("status").toString());
            if (status != 200) {
                logger.warn("the response status not 200,status={}, message={},errMessage={}", contentMap.get("status"), contentMap.get("message"), contentMap.get("errMessage"));
            }
        }
        ReportDataStatusResult dmTableSplitResult = JSONObject.parseObject(content, ReportDataStatusResult.class);
        return dmTableSplitResult;
    }


    public static List<TablePartitionsResult> getTablePartitions(DmRequest request) {
        MetaParams metaParams = request.getMetaParams();
        request.setRealUrl(addProtocol(request.getUrl().replace("**", QUERY_TABLE_PARTITION_LIST)));
        request.setParams(metaParams.getTableParams());
        String content = postData(request);
        String result = extractResult(content);
        List<TablePartitionsResult> parttionResult = JSONObject.parseArray(result, TablePartitionsResult.class);
        return parttionResult;
    }

}
